🎖️GitЯра🎖️
docs/en/developer/adding-a-feature-module.md bd2863243bab6eb213401d949839a2bc74dde7e2 (bd286324) Text, 3.74 KB
---
title: Adding a Feature Module
parent: Developer Guide
nav_order: 3
last_updated: 2026-07-08
aliases:
Tff7b72- new-module
Tff7b72- feature-module
Tc9d1d9 - module-guide
Tc9d1d9---
Tc9d1d9# Adding a Feature Module
Step-by-step guide for creating a new KMP feature module in the Meshtastic project.
Tc9d1d9## 1. Create the Module Directory
Ta5d6ff```Ta5d6ffbash
mkdir -p feature/my-feature/src/Tff7b72{commonMain,commonTest,androidMain,jvmMain,iosMainTff7b72}/kotlin/org/meshtastic/feature/myfeature
Ta5d6ff```
Tc9d1d9## 2. Create `build.gradle.kts`
Ta5d6ff```Ta5d6ffkotlin
Te6edf3plugins Tb4b4b4{
Te6edf3aliasTb4b4b4(Te6edf3libsTb4b4b4.Te6edf3pluginsTb4b4b4.Te6edf3meshtasticTb4b4b4.Te6edf3kmpTb4b4b4.Te6edf3featureTb4b4b4)
Tb4b4b4}
Te6edf3kotlin Tb4b4b4{
Te6edf3android Tb4b4b4{ Te6edf3withHostTest Tb4b4b4{ Te6edf3isIncludeAndroidResources Tff7b72= Tff7b72true Tb4b4b4} Tb4b4b4}
Te6edf3sourceSets Tb4b4b4{
Te6edf3commonMainTb4b4b4.Te6edf3dependencies Tb4b4b4{
Te6edf3implementationTb4b4b4(Te6edf3projectsTb4b4b4.Te6edf3coreTb4b4b4.Te6edf3commonTb4b4b4)
Te6edf3implementationTb4b4b4(Te6edf3projectsTb4b4b4.Te6edf3coreTb4b4b4.Te6edf3navigationTb4b4b4)
Te6edf3implementationTb4b4b4(Te6edf3projectsTb4b4b4.Te6edf3coreTb4b4b4.Te6edf3resourcesTb4b4b4)
Te6edf3implementationTb4b4b4(Te6edf3projectsTb4b4b4.Te6edf3coreTb4b4b4.Te6edf3uiTb4b4b4)
Te6edf3implementationTb4b4b4(Te6edf3projectsTb4b4b4.Te6edf3coreTb4b4b4.Te6edf3diTb4b4b4)
Tb4b4b4}
Te6edf3commonTestTb4b4b4.Te6edf3dependencies Tb4b4b4{
Te6edf3implementationTb4b4b4(Te6edf3libsTb4b4b4.Te6edf3composeTb4b4b4.Te6edf3multiplatformTb4b4b4.Te6edf3uiTb4b4b4.Te6edf3testTb4b4b4)
Tb4b4b4}
Te6edf3jvmTestTb4b4b4.Te6edf3dependencies Tb4b4b4{
Te6edf3implementationTb4b4b4(Te6edf3composeTb4b4b4.Te6edf3desktopTb4b4b4.Te6edf3currentOsTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Ta5d6ff```
Tc9d1d9## 3. Register in `settings.gradle.kts`
Add your module to the main Ta5d6ff`include()` block:
Ta5d6ff```Ta5d6ffkotlin
Te6edf3includeTb4b4b4(
T8b949e// ...existing modules...
Ta5d6ff"Ta5d6ff:feature:my-featureTa5d6ff"Tb4b4b4,
Tb4b4b4)
Ta5d6ff```
Tc9d1d9## 4. Create the DI Module
Ta5d6ff`src/commonMain/kotlin/org/meshtastic/feature/myfeature/di/FeatureMyFeatureModule.kt`:
Ta5d6ff```Ta5d6ffkotlin
Tff7b72package T7ee787org.meshtastic.feature.myfeature.di
Tff7b72import T7ee787org.koin.core.annotation.ComponentScan
Tff7b72import T7ee787org.koin.core.annotation.Module
Tf0883e@Module
Tf0883e@ComponentScanTb4b4b4(Ta5d6ff"Ta5d6fforg.meshtastic.feature.myfeatureTa5d6ff"Tb4b4b4)
Tff7b72class T56d364FeatureMyFeatureModule
Ta5d6ff```
Tc9d1d9## 5. Register DI in App/Desktop
Add your module to:
Tff7b72- Ta5d6ff`androidApp/src/main/kotlin/org/meshtastic/app/di/AppKoinModule.kt`
Tff7b72- Ta5d6ff`desktopApp/src/main/kotlin/org/meshtastic/desktop/di/DesktopKoinModule.kt`
Tc9d1d9## 6. Add Navigation Routes
In Ta5d6ff`core/navigation/src/commonMain/kotlin/org/meshtastic/core/navigation/Routes.kt`:
Ta5d6ff```Ta5d6ffkotlin
Tf0883e@Serializable
Tff7b72sealed Tff7b72interface T56d364MyFeatureRoute Tb4b4b4: Te6edf3Route Tb4b4b4{
Tf0883e@Serializable Tff7b72data Tff7b72object T56d364MyFeatureGraph Tb4b4b4: Te6edf3MyFeatureRouteTb4b4b4, Te6edf3Graph
Tf0883e@Serializable Tff7b72data Tff7b72object T56d364MyFeatureHome Tb4b4b4: Te6edf3MyFeatureRoute
Tb4b4b4}
Ta5d6ff```
Tc9d1d9## 7. Create Navigation Entries
Ta5d6ff`src/commonMain/kotlin/org/meshtastic/feature/myfeature/navigation/MyFeatureNavigation.kt`:
Ta5d6ff```Ta5d6ffkotlin
Tff7b72package T7ee787org.meshtastic.feature.myfeature.navigation
Tff7b72import T7ee787androidx.navigation3.runtime.EntryProviderScope
Tff7b72import T7ee787androidx.navigation3.runtime.NavBackStack
Tff7b72import T7ee787androidx.navigation3.runtime.NavKey
Tff7b72import T7ee787org.meshtastic.core.navigation.MyFeatureRoute
Tff7b72fun Td2a8ffEntryProviderScopeTff7b72<Te6edf3NavKeyTff7b72>Tb4b4b4.Te6edf3myFeatureGraphTb4b4b4(Te6edf3backStackTb4b4b4: Te6edf3NavBackStackTff7b72<Te6edf3NavKeyTff7b72>Tb4b4b4) Tb4b4b4{
Te6edf3entryTff7b72<Te6edf3MyFeatureRouteTb4b4b4.Te6edf3MyFeatureHomeTff7b72> Tb4b4b4{
Te6edf3MyFeatureScreenTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Ta5d6ff```
Tc9d1d9## 8. Source Set Guidelines
| Source Set | Contains |
|-----------|----------|
| Ta5d6ff`commonMain` | Models, ViewModels, shared UI, DI module, navigation |
| Ta5d6ff`androidMain` | Android-specific implementations (e.g., platform APIs) |
| Ta5d6ff`jvmMain` | Desktop-specific implementations |
| Ta5d6ff`iosMain` | iOS-specific implementations |
| Ta5d6ff`commonTest` | Shared unit tests |
Tc9d1d9## 9. Testing Expectations
Every feature module should have:
Tff7b72- Unit tests in Ta5d6ff`commonTest` for business logic
Tff7b72- UI tests using Ta5d6ff`compose-multiplatform-ui-test` where appropriate
Tff7b72- No test dependency on other feature modules
Tc9d1d9## 10. Checklist
Tff7b72- Tff7b72[ ] Module directory created
Tff7b72- Tff7b72[ ] Ta5d6ff`build.gradle.kts` with correct plugins and dependencies
Tff7b72- Tff7b72[ ] Added to Ta5d6ff`settings.gradle.kts`
Tff7b72- Tff7b72[ ] DI module created with Ta5d6ff`@ComponentScan`
Tff7b72- Tff7b72[ ] DI module registered in app and desktop roots
Tff7b72- Tff7b72[ ] Routes added to Ta5d6ff`Routes.kt`
Tff7b72- Tff7b72[ ] Navigation entries registered
Tff7b72- Tff7b72[ ] Ta5d6ff`./gradlew kmpSmokeCompile` passes
Tff7b72- Tff7b72[ ] Ta5d6ff`./gradlew :feature:my-feature:allTests` passes
---
Served by rngit 1.5.0 - Generated in 0.12s